package a02;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

public class TestBeanFactory {

    public static void main(String[] args) {

        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        /**
         * BeanDefinition是什么呢? 其包括(class,scope,初始化，销毁)
         * beanFactory管理创建对象，应该就是根据BeanDefinition的各个信息来的
         */
        //创建BeanDefinition并把它注册到beanFactory
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        beanFactory.registerBeanDefinition("config", beanDefinition);

        /**
         * 上图所示，把Config的bean定义注册进去以后，config确实有了，但config里面被@Bean注解
         * 的类的beanDefinition并没有看到，说明并没有被解析出来。那么是谁来解析的呢？后处理器
         */
        //给beanFactory添加一些常用的后处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().stream().forEach(process -> {
            System.out.println(process);
        });

        /**
         * 需要主动去运行"BeanFactoryPostProcessor"，才能获得注解标识的Bean的BeanDefinition.可以看到现在@Bean注解标注的bean也有了
         * 这就是由internalConfigurationAnnotationProcessor去解析的。可见，这些后处理器的功能主要是对BeanDefinition
         * 的补充。
         */

        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().stream().forEach(process -> {
            process.postProcessBeanFactory(beanFactory);
        });


        System.out.println("--------------");

        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        /**
         * beanFactory在创建bean时并不能识别里面的@Autowired、@Resource等注解，而完成这些自动
         * 注入功能的，就是这些Bean后处理器(BeanPostProcessor),如internalAutowiredAnnotationProcessor
         * 时识别@Autowired的，而internalCommonAnnotationProcessor时识别@Resource的（@Resource是第三方
         * 的注解，所以命名中CommonAnnotation也比较好理解）。
         * 需要把"BeanPostProcessor"器添加到beanFactory，这样，创建BeanA时，BeanA中自动注入的BeanB才不是null
         */
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanPostProcessor -> {
            System.out.println(">>>>>>>>>" + beanPostProcessor);
            beanFactory.addBeanPostProcessor(beanPostProcessor);
        });

        /**
         * 一般来说，beanFactory时在真正用到bean是才会创建bean。如果想让他提前把那些单列bean创建好，可以使用
         * preInstantiateSingletons个方法
         * 综上，下面的功能beanFactory都不会主动去做，而是通过一些后处理器来完成的。
         *  学到了什么:
         *  a. beanFactory 不会做的事
         *         1. 不会主动调用 BeanFactory 后处理器
         *         2. 不会主动添加 Bean 后处理器
         *         3. 不会主动初始化单例
         *         4. 不会解析beanFactory 还不会解析 ${ } 与 #{ }
         *  b. bean 后处理器会有排序的逻辑
         */
        beanFactory.preInstantiateSingletons();

        System.out.println("-----------------------");

        System.out.println(beanFactory.getBean(Config.class));
        System.out.println(beanFactory.getBean(BeanA.class).getBeanB());
        System.out.println(beanFactory.getBean(BeanA.class).getIbean());





    }



    /**
     * 类前需要加static，否则在main函数里通过spring的getBean获取Config的对象会报创建
     * bean失败，找不到默认的构造函数的一场。至于原理，我猜测就如同在静态的main方法里调用
     * 同类的方法必须时，该方法必须是静态的。而这个内部类如果不用static修饰，他的构造方法
     * 也是非静态的，所以在main方法里调用不了。
     * 当然，如果把内部类提出来作为一个单独的类，就不需要考虑这些呢，所以我觉得注释掉下面的，
     * 把类提出来做为单独的类
     */
//    @Configuration
//    static class Config{
//
//
//        @Bean
//        public BeanA beanAA(){
//            return new BeanA();
//        }
//    }
//
//    static class BeanA{
//
//        public BeanA() {
//            System.out.println("BeanA构造函数");
//        }
//
//        @Autowired
//        public BeanB beanB;
//    }
//
//    static class BeanB{
//
//        public BeanB() {
//
//        }
//    }
}
